//
//  MFAMessageQueue.m
//  NewMissFresh
//
//  Created by 董宝君 on 2019/6/12.
//  Copyright © 2019 com.missfresh. All rights reserved.
//

#import "MFAMessageQueue.h"
#import "MFALogger.h"
#import <sqlite3.h>
#import "MFAnalytics.h"
#import "MFAConstants.h"
#import "MFAExceptionReporter.h"

@interface MFAMessageQueue ()
@end

@implementation MFAMessageQueue {
    sqlite3 *_database;     //数据库对象
    NSInteger _messageCount;    //消息数
    CFMutableDictionaryRef _dbStmtCache;    //dbmt缓存
}

#pragma mark - init

- (instancetype)initWithFilePath:(NSString *)filePath {
    if(self = [super init]){
        if(sqlite3_initialize() != SQLITE_OK){
            MFAError(@"failed to initialize SQLite. dataCache");
            [[MFAExceptionReporter sharedReporter] reportMessage:@"failed to initialize SQLite. dataCache" pageName:MF_ANALYTICS_ERROR_MODULE_SQLITE];
            return nil;
        }
        if(sqlite3_open_v2([filePath UTF8String], &_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL) == SQLITE_OK){
            //创建表
            NSString *sqlString = @"create table if not exists dataCache (id INTEGER PRIMARY KEY AUTOINCREMENT, type TEXT, content TEXT)";
            char *errorMsg;
            if (sqlite3_exec(_database, [sqlString UTF8String], NULL, NULL, &errorMsg) == SQLITE_OK) {
                MFADebug(@"Create dataCache Success.");
            } else {
                MFAError(@"Create dataCache Failure %s", errorMsg);
                [[MFAExceptionReporter sharedReporter] reportMessage:[NSString stringWithFormat:@"Create dataCache Failure %s",errorMsg] pageName:MF_ANALYTICS_ERROR_MODULE_SQLITE];
                return nil;
            }
            CFDictionaryKeyCallBacks keyCallbacks = kCFCopyStringDictionaryKeyCallBacks;
            CFDictionaryValueCallBacks valueCallbacks = { 0 };
            _dbStmtCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &keyCallbacks, &valueCallbacks);
            
            _messageCount = [self sqliteCount];
            
            MFADebug(@"SQLites is opened. current count is %ul", _messageCount);
        }else{
            if (_dbStmtCache) CFRelease(_dbStmtCache);
            _dbStmtCache = NULL;
            
            MFAError(@"failed to open SQLite db.");
            [[MFAExceptionReporter sharedReporter] reportMessage:@"failed to open SQLite db." pageName:MF_ANALYTICS_ERROR_MODULE_SQLITE];
            return nil;
        }
    }
    return self;
}

#pragma mark - public

- (void)addMessage:(NSString *)message type:(NSString *)type {
    //另外现在定义业务的超出规定上限的逻辑不合理
    UInt64 maxCacheSize = [MFAnalytics sharedAnalytics].config.maxCacheSize;
    if(_messageCount >= maxCacheSize){
        MFAError(@"touch MAX_MESSAGE_SIZE:%d, try to delete some old events", maxCacheSize);
        [[MFAExceptionReporter sharedReporter] reportMessage:[NSString stringWithFormat:@"touch MAX_MESSAGE_SIZE:%@, try to delete some old events",@(maxCacheSize)] pageName:MF_ANALYTICS_ERROR_MODULE_LOSE_DATA];
        BOOL result = [self removeMessageFromHeadWithMessageSize:100 type:@"POST"];
        if(result){
            _messageCount = [self sqliteCount];
        }else{
            MFAError(@"touch MAX_MESSAGE_SIZE:%d, try to delete some old events FAILED", maxCacheSize);
            [[MFAExceptionReporter sharedReporter] reportMessage:[NSString stringWithFormat:@"touch MAX_MESSAGE_SIZE:%@, try to delete some old events FAILED",@(maxCacheSize)] pageName:MF_ANALYTICS_ERROR_MODULE_SQLITE];
            return;
        }
    }
    
    NSString *query = @"INSERT INTO dataCache(type, content) values(?, ?)";
    sqlite3_stmt *insertStatement = [self dbCacheStmt:query];
    int rc;
    if (insertStatement && message.length > 0) {
        sqlite3_bind_text(insertStatement, 1, [type UTF8String], -1, SQLITE_TRANSIENT);
        @try {
            sqlite3_bind_text(insertStatement, 2, [message UTF8String], -1, SQLITE_TRANSIENT);
        } @catch (NSException *exception) {
            MFAError(@"Found NON UTF8 String, ignore");
            [[MFAExceptionReporter sharedReporter] reportMessage:@"Found NON UTF8 String, ignore" pageName:MF_ANALYTICS_ERROR_MODULE_LOSE_DATA];
            return;
        }
        rc = sqlite3_step(insertStatement);
        if (rc != SQLITE_DONE) {
            MFAError(@"insert into dataCache fail, rc is %d", rc);
            [[MFAExceptionReporter sharedReporter] reportMessage:[NSString stringWithFormat:@"insert into dataCache fail, rc is %d",rc] pageName:MF_ANALYTICS_ERROR_MODULE_LOSE_DATA];
        } else {
            _messageCount++;
            MFADebug(@"insert into dataCache success, current count is %lu", _messageCount);
        }
    } else {
        MFAError(@"insert into dataCache error");
        [[MFAExceptionReporter sharedReporter] reportMessage:@"insert into dataCache error" pageName:MF_ANALYTICS_ERROR_MODULE_LOSE_DATA];
    }
}

- (NSArray <NSString *> *)loadMessageFromHeadWithMessageSize:(NSUInteger)size type:(NSString *)type {
    if (_messageCount == 0) {
        return @[];
    }
    NSMutableArray *contentArray = [NSMutableArray array];
    NSString *query = [NSString stringWithFormat:@"SELECT id,content FROM dataCache ORDER BY id ASC LIMIT %lu", (unsigned long)size];
    
    sqlite3_stmt *stmt = [self dbCacheStmt:query];
    if (stmt) {
        while (sqlite3_step(stmt) == SQLITE_ROW) {
            @try {
                char *messageChar = (char *)sqlite3_column_text(stmt, 1);
                if (!messageChar) {
                    MFAError(@"Failed to query column_text, error:%s", sqlite3_errmsg(_database));
                    [[MFAExceptionReporter sharedReporter] reportMessage:[NSString stringWithFormat:@"Failed to query column_text, error:%s",sqlite3_errmsg(_database)] pageName:MF_ANALYTICS_ERROR_MODULE_SQLITE];
                    return nil;
                }
                NSString *messageString = [NSString stringWithUTF8String:messageChar];
                if (messageString.length) {
                    [contentArray addObject:messageString];
                } else {
                    char *idChar = (char *)sqlite3_column_text(stmt, 0);
                    NSInteger index = [[NSString stringWithUTF8String:idChar] integerValue];
                    [self deleteMessageWithId:index];
                }
            } @catch (NSException *exception) {
                MFAError(@"Found NON UTF8 String, ignore");
                [[MFAExceptionReporter sharedReporter] reportMessage:@"Found NON UTF8 String, ignore" pageName:MF_ANALYTICS_ERROR_MODULE_SQLITE];
            }
        }
    } else {
        MFAError(@"Failed to prepare statement, error:%s", sqlite3_errmsg(_database));
        [[MFAExceptionReporter sharedReporter] reportMessage:[NSString stringWithFormat:@"Failed to prepare statement, error:%s",sqlite3_errmsg(_database)] pageName:MF_ANALYTICS_ERROR_MODULE_SQLITE];
        return nil;
    }
    return [contentArray copy];
}

- (BOOL)removeMessageFromHeadWithMessageSize:(NSUInteger)size type:(NSString *)type {
    NSUInteger removeSize = MIN(size, _messageCount);
    NSString *query = [NSString stringWithFormat:@"DELETE FROM dataCache WHERE id IN (SELECT id FROM dataCache ORDER BY id ASC LIMIT %lu);", (unsigned long)removeSize];
    char *errMsg;
    @try {
        if (sqlite3_exec(_database, [query UTF8String], NULL, NULL, &errMsg) != SQLITE_OK) {
            MFAError(@"Failed to delete record msg = %s", errMsg);
            [[MFAExceptionReporter sharedReporter] reportMessage:[NSString stringWithFormat:@"Failed to delete record msg = %s",errMsg] pageName:MF_ANALYTICS_ERROR_MODULE_SQLITE];
            return NO;
        }
    } @catch (NSException *exception) {
        MFAError(@"Failed to delete record exception=%@", exception);
        [[MFAExceptionReporter sharedReporter] reportMessage:[NSString stringWithFormat:@"Failed to delete record exception=%@",exception] pageName:MF_ANALYTICS_ERROR_MODULE_SQLITE];
        return NO;
    }
    _messageCount = [self sqliteCount];
    return YES;
}

- (void)deleteAllMessage {
    NSString *query = @"DELETE FROM dataCache";
    char *errMsg;
    @try {
        if (sqlite3_exec(_database, [query UTF8String], NULL, NULL, &errMsg) != SQLITE_OK) {
            MFAError(@"Failed to delete record msg=%s", errMsg);
            [[MFAExceptionReporter sharedReporter] reportMessage:[NSString stringWithFormat:@"Failed to delete record msg=%s",errMsg] pageName:MF_ANALYTICS_ERROR_MODULE_SQLITE];
        }
    } @catch (NSException *exception) {
        MFAError(@"Failed to delete record exception=%@", exception);
        [[MFAExceptionReporter sharedReporter] reportMessage:[NSString stringWithFormat:@"Failed to delete record exception=%@",exception] pageName:MF_ANALYTICS_ERROR_MODULE_SQLITE];
    }
    
    _messageCount = [self sqliteCount];
}

- (NSInteger)messageCount {
    return _messageCount;
}

- (BOOL)vacuum {
#ifdef SENSORS_ANALYTICS_ENABLE_VACUUM
    @try {
        NSString *query = @"VACUUM";
        char *errMsg;
        if (sqlite3_exec(_database, [query UTF8String], NULL, NULL, &errMsg) != SQLITE_OK) {
            MFAError(@"Failed to vaccum msg=%s", errMsg);
            [[MFAExceptionReporter sharedReporter] reportMessage:[NSString stringWithFormat:@"Failed to vaccum msg=%s",errMsg] pageName:MF_ANALYTICS_ERROR_MODULE_SQLITE];
            return NO;
        }
        return YES;
    } @catch (NSException *exception) {
        return NO;
    }
#else
    return YES;
#endif
}

#pragma mark - private

- (BOOL)deleteMessageWithId:(NSInteger)index {
    NSString *queryDelete = [NSString stringWithFormat:@"DELETE FROM dataCache WHERE id = %ld", (long)index];
    
    char *errDeleteMsg;
    if (sqlite3_exec(_database, [queryDelete UTF8String], NULL, NULL, &errDeleteMsg) == SQLITE_OK) {
        _messageCount--;
        return YES;
    } else {
        MFAError(@"Failed to delete record msg=%s", errDeleteMsg);
        [[MFAExceptionReporter sharedReporter] reportMessage:[NSString stringWithFormat:@"Failed to delete record msg=%s",errDeleteMsg] pageName:MF_ANALYTICS_ERROR_MODULE_SQLITE];
        return NO;
    }
}

- (NSInteger)sqliteCount {
    NSString *query = @"select count(*) from dataCache";
    NSInteger count = 0;
    sqlite3_stmt *statement = [self dbCacheStmt:query];
    if (statement) {
        while (sqlite3_step(statement) == SQLITE_ROW)
            count = sqlite3_column_int(statement, 0);
    } else {
        MFAError(@"Failed to prepare statement");
        [[MFAExceptionReporter sharedReporter] reportMessage:@"Failed to prepare statement" pageName:MF_ANALYTICS_ERROR_MODULE_SQLITE];
    }
    return count;
}

- (sqlite3_stmt *)dbCacheStmt:(NSString *)sql {
    if (sql.length == 0 || !_dbStmtCache) return NULL;
    sqlite3_stmt *stmt = (sqlite3_stmt *)CFDictionaryGetValue(_dbStmtCache, (__bridge const void *)(sql));
    if (!stmt) {
        int result = sqlite3_prepare_v2(_database, sql.UTF8String, -1, &stmt, NULL);
        if (result != SQLITE_OK) {
            MFAError(@"%s line:%d sqlite stmt prepare error (%d): %s", __FUNCTION__, __LINE__, result, sqlite3_errmsg(_database));
            [[MFAExceptionReporter sharedReporter] reportMessage:[NSString stringWithFormat:@"%s line:%d sqlite stmt prepare error (%d): %s",__FUNCTION__, __LINE__, result, sqlite3_errmsg(_database)] pageName:MF_ANALYTICS_ERROR_MODULE_SQLITE];
            return NULL;
        }
        CFDictionarySetValue(_dbStmtCache, (__bridge const void *)(sql), stmt);
    } else {
        sqlite3_reset(stmt);
    }
    return stmt;
}

//关闭数据库
- (void)closeDatabase {
    if(_dbStmtCache){
        CFRelease(_dbStmtCache);
    }
    _dbStmtCache = NULL;
    
    sqlite3_close(_database);
    sqlite3_shutdown();
    MFADebug(@"%@ close database",self);
}

- (void)dealloc {
    [self closeDatabase];
}

@end
